#ifndef __INTERNALGSERVER__
#define __INTERNALGSERVER__

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <unistd.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>/*fd_set types*/

#include <fcntl.h>/*for fctrl makes sockets not blocked*/
#include <string.h> /*for memset*/
#include <assert.h>

#include "genserver.h"
#include "bittreeiter.h"
#include "List.h"

#include "Queueinside.h"
#include "scheduller.h" /* These includes need for Scheduller , at this time i don't whant them*/


#define   BAKCLOG         (5)
#define   INOUT
#define   OUT
#define   DEADBEAF        (void*)0xdeadbeaf

enum isStop{NOT_STOP,STOP};
enum isAlldone{ NOT_DONE,DONE};
enum IOMODE{READ,WRITE};
enum isHeaderReaded{HEADER_NOT_READEN,READEN};
enum isDropt{NOT_DROP,DROP};
enum protType{TCP_PR,UDP_PR};

typedef long int key;


typedef struct IOBlock_t
{
    size_t     m_donesize;/*designates the number of bytes were allready transfered*/
    size_t     m_rest;/* disignates the nubmer of bytes not still to transfer, initialli it is the size of message*/
    char*      m_buf;
}IOblock;


IOblock    NULLIO={0};/*use fo initiallizig of IO blocks*/

typedef struct Client_t
{
    int             m_fd;
    int             m_isAlldone;/*The status is all action
                                 protected client from possible drops are completed
                                 At this version it can't be removed without finishig all writing tasks*/
    int             m_isdrop;/*This flage set to 1 or when client disconnected or when
                                application wants to disconnect it.After its set only operatio of Sending messages prviously accepted
                                from application to client can be done. After all messages sent the client would be removed with deallocation
                                 of all relevant resourses*/
    char*           m_headerbuffer;
    int             m_isheadrd;/*Flag set 1 if header was readed and 0 if not*/
    IOblock         m_readIO;
    IOblock         m_oobIO;

    List*           m_messageQ;/*List of messages structs with
                                    messages from Server size and buf*/
} Client;



typedef struct UDPClient_t
{
    key      m_key;
    int      m_IP;
    int      m_port;
    Client   m_cln;
}UDPClient;

typedef struct PortHandelr_t
{
    short     m_port;
    Tree*     m_UDPClient;
} PHUdp;



struct GenericServer_t
{
    int             m_portTCP;
    int             m_portUDP;
    int             m_servSockfdTCP;
    int             m_servSockfdUDP;
    int             m_isStop;
    SchSched*       m_Sched;

    void*           m_appl;
    size_t          m_headSize;
    size_t          m_exmesSize;

    int             m_backlog;
    HeaderFunc      m_headF;

    NewClientCb     m_newClF;
    NewMesssageCb   m_newMesF;
    ClientDiscnCb   m_clDiscF;
    NewExeptionCb   m_newExepF;

    Tree*           m_clientT;
    Tree*           m_porthahdlerUDP;
};


/****************Declaration**of**Internal**Funcitons*******************************/
/*Initillize server socket: create, set up different socket options,
 bind it and  insert it to listening
 Returns servSockfd or -1 on fails*/
static int  GsvInit(int _port,int _mode);

static int  GsvSocketNewTCP(int _port);
static void GsvBind(int _socket,int _port);
/*returns 0 on success and 1 on error*/
static int  GsvListentoClient(int _socket);
static void GsvSetSockNonBlock(int _socket);

/*Reset the fd_set before each select loop
Insert the existed file descriptors into fd_set
Returns the MaxFileDescriptor + 1 used as 1st argument of select funciton*/
static int GsvBuildSelectList(GenSv* _this, fd_set* _fd_rd,

                                fd_set* _fd_wr,fd_set* _fd_ex);

/*If o.k returns 0 else returns error log*/
static void  GsvUseReadySocket(GenSv* _this,fd_set* _fd_rd,
                              fd_set* _fd_wr, fd_set* _fd_ex, OUT err_Gsv* _error);

/*Get the fd of new client, allocate struct client, send the Callback to application
Returns 0 if success and error log (err_Gsv) if fails*/
static err_Gsv GsvAcceptNewClient(GenSv* _this, OUT TreeIter* _newClientIter);
/*returns null pointer if error*/
static Client* GsvNewClient(int _newclientfd,int _buffersize);
/*Iterator on newClient*/
static TreeIter GsvAddNewClientandCB(GenSv*_this,Client* _newclient);
/*Deallocat all allocated resourses by client and deallocate it self after*/
static void GsvclientDestroy(Client* _Client);
/*returns fd of new connection*/
static int MakeNewConnection(int _servSocket);
/*returns error log*/
static int GsvSend(GenSv* _this,Client* _client);
/*returns error log*/
static int GsvRecvOOB(Client* _client);
/*return Iterator of next to removed client*/
static TreeIter  GsvDropClients(Tree* ,TreeIter _dropClientItr);
/*Reads message from Client initially caclulate
the total package lenght by reading the header.If obtained value from Header
less is 0 or less disconnect client, any other value of message pack is valid.
thatAfter message is recieved activate the CallBack funciotn NewMesssageCb.
Returns error log*/
static int GsvRecv(GenSv* _this, Client* _client);

/*This funciton perform read or write IO aciton, depended by  _mode (READ==0 and WRITE==1). The _fdclient
specified the fd of socket wich will be used. After performing the IO action the function updates the
fieldes (m_donesioze or m_restsize)  of corresponded readIO or writeIO block. It returns nuber of bytes
readed or writed respectively.*/
static int GsvIOandUpdate(int _fdclient,IOblock* _ioblock,int _mode);
/*Activate the CallBack and set the flag is_alldone == DONE to done is_drop == DROP*/
static void GsvClientPrepare2Disconnect(GenSv* _this,Client* _client);
static void GsvResetIO(IOblock* _ioblock);
/*if fails on malloc returns 1, if ok returns 0. copy message from application to messageQ*/
static int GsvSetIO(IOblock* _message, char* _buffer, size_t _size);


/**************************Scheduller funcitons***********************************/
static void GsvWaitTime(struct timeval* _wait, SchSched* _Sch);


/*************UDP connection funtions*******************************************************/

/*Check is the client with provided IP and Port already existed in data tree and returns pointer
to UDPClient or 0 if object not existed*/
UDPClient* GsvFindUDPClient( Tree*  _porthadlerTUDP,int _sockfd,short _port,int _IP );

static PHUdp* GsvFindUDPClientByPort(Tree*  _porthadlerTUDP, PHUdp* _portdummy);

static UDPClient* GsvFindUDPClientByIP(Tree*  _UDPClientrT, UDPClient*  _uclindummy);

/*Creates the new UDPClient struct , filled the IP and Port and key instert it to in a tree,
returns pointer to new UDPClient, in case of one of allocations failes returns 0 and prints
error to stderr*/
static UDPClient* GsvInsertUDPClient( Tree*  _porthahdlerUDP,int _sockfd,short _port,int _IP );

#endif /* INTERNALGSERVER_H_INCLUDED*/
